let activeEffect = undefined;

const bucket = new WeakMap();
// 追踪
const track = (target, key) => {
  if (!activeEffect) return;

  let depsMap = bucket.get(target); // WeakMap 由 target --> Map 构成；
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()));
  }
  let deps = depsMap.get(key); // Map 由 key --> Set 构成
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }
  // 最后将当前激活的副作用函数添加到桶中
  deps.add(activeEffect);

  // deps就是一个与当前副作用函数存在关联的依赖集合
  activeEffect.deps.push(deps);
};
// 触发
const trigger = (target, key) => {
  let depsMap = bucket.get(target);
  if (!depsMap) return;
  let deps = depsMap.get(key);
  // 执行副作用函数
  const effectSet = new Set(deps);

  const effectSetRun = new Set();
  effectSet &&
    effectSet.forEach((fn) => {
      // 如果trigger触发的副作用函数与当前正在执行的副作用函数是同一个 则不触发执行
      if (fn !== activeEffect) {
        effectSetRun.add(fn);
      }
    });

  // [4.7] 增加调度器
  effectSetRun &&
    effectSetRun.forEach((effectFn) => {
      // [4.7]如果存在调度器，则直接调用调度器函数 并把当前副作用函数传递过去，由用户自己控制副作用函数的执行(把控制权交给用户)
      if (effectFn.options.scheduler) {
        effectFn.options.scheduler(effectFn);
      } else {
        effectFn();
      }
    });
};

// 定义一个栈来存储副作用函数
const effectStack = [];
const effect = (fn, options = {}) => {
  const _effectFn = () => {
    cleanup(_effectFn);
    activeEffect = _effectFn;
    // 将当前副作用函数压入栈中
    effectStack.push(_effectFn);
    fn();
    // 副作用函数执行完之后 将其从栈中弹出
    effectStack.pop();
    // activeEffect始终指向栈顶的副作用函数
    activeEffect = effectStack[effectStack.length - 1];
  };
  _effectFn.options = options;
  //  用来存储 所有与当前副作用函数相关联的依赖集合
  _effectFn.deps = [];

  _effectFn();
};

const cleanup = (effectFn) => {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i];
    deps.delete(effectFn);
  }
  effectFn.deps.length = 0;
};

// 4.7 调度执行

// 测试代码
const data = {
  foo: 1,
};
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key);
    return target[key];
  },
  set(target, key, val) {
    target[key] = val;
    trigger(target, key);
    return true;
  },
});

// effect(
//   () => {
//     console.log("obj.foo==", obj.foo);
//   },
//   {
//     scheduler(fn) {
//       // console.log("trigger==", fn);
//       setTimeout(fn, 0);
//     },
//   }
// );
// obj.foo += 1;
// console.log("执行完毕==");
// 增加调度器可以将副作用函数的执行控制权由用户控制（测试代码fn在setTimeout中执行，是把副作用函数放在了事件循环队列中）

// 测试代码2
const data2 = {
  bar: 1,
};
const obj2 = new Proxy(data2, {
  get(target, key) {
    track(target, key);
    return target[key];
  },
  set(target, key, val) {
    target[key] = val;
    trigger(target, key);
    return true;
  },
});
const jobQueue = new Set();
const p = Promise.resolve();
let isFlushing = false;
// flushJob在一个事件循环内只会执行一次
const flushJob = () => {
  if (isFlushing) return;
  isFlushing = true;
  p.then(() => {
    jobQueue.forEach((jobFn) => jobFn());
  }).finally(() => {
    isFlushing = false;
  });
};
effect(
  () => {
    console.log("obj2.bar==", obj2.bar);
  },
  {
    scheduler(fn) {
      jobQueue.add(fn);
      flushJob();
    },
  }
);
obj2.bar++;
obj2.bar++;
